import {
    NodeDefine,
    LineDefine,
    ChangeSocketValue,
    Socket,
} from "./interface/index.ts";
import processError from "../error/index.ts";
import { IDentity } from "../types/propert.ts";
import Node from "./node.ts";
import Line from "./line.ts";
import isEmpty from "../utils/objects.ts";

/** 状态 */
class State {
    nodeList: { [id: string]: Node } = {};
    lineList: { [id: string]: Line } = {};

    /**
     * 添加节点
     * @param node 节点定义
     */
    addNode(node: NodeDefine): IDentity {
        let n = new Node(node);
        this.nodeList[n.id] = n;
        return n.id;
    }

    /**
     * 添加线
     * @param lineDefine 线定义
     */
    addLine(lineDefine: LineDefine): boolean | IDentity {
        if (this.canAddLine(lineDefine) !== true) return false;
        let { headNodeID, tailNodeID } = lineDefine;
        let headNode = this.nodeList[headNodeID];
        let tailNode = this.nodeList[tailNodeID];

        let line = new Line(lineDefine);
        this.lineList[line.id] = line;

        tailNode.outLines[line.id] = line.id;
        headNode.inLines[line.id] = line.id;

        return line.id;
    }

    /**
     * 移除节点
     * @param id 节点 id
     */
    removeNode(id: IDentity) {
        // 先移除节点关联线
        for (let lineID in this.nodeList[id].inLines) {
            this.removeLine(lineID);
        }
        for (let lineID in this.nodeList[id].outLines) {
            this.removeLine(lineID);
        }
        delete this.nodeList[id];
    }

    /**
     * 移除线
     * @param id 线 id
     */
    removeLine(id: IDentity) {
        let line = this.lineList[id];
        let headNodeID = line.headNodeID;
        let tailNodeID = line.tailNodeID;
        let handNode = this.nodeList[headNodeID];
        let tailNode = this.nodeList[tailNodeID];
        let headSocketIndex = line.headSocketIndex;

        // 分别处理简单值与复杂值 从节点线列表中移除线 将插槽归位
        let socket = handNode.sockets[headSocketIndex];
        if (socket.isComplex === false) {
            delete handNode.inLines[id];
            delete handNode.outLines[id];
            delete tailNode.inLines[id];
            delete tailNode.outLines[id];
            socket.value = undefined;
        } else {
            delete handNode.inLines[id];
            delete handNode.outLines[id];
            delete tailNode.inLines[id];
            delete tailNode.outLines[id];
            delete socket.value[id];
        }
        delete this.lineList[id];
    }

    /**
     * 改变节点插槽值
     * @param options
     * @returns 位于插槽上的线索引
     */
    changeNodeSocketValue(options: ChangeSocketValue): IDentity[] {
        let { nodeID, socketIndex, value, valueIndex } = options;
        let node = this.nodeList[nodeID];
        let sockets = node.sockets;
        let socket = sockets[socketIndex];
        let outLines = node.outLines;
        let outLineIndexs = [];
        for (let index in outLines) {
            let id = outLines[index];
            outLineIndexs.push(id);
        }

        // 设置值 分别处理简单和复杂模式
        if (socket.mode !== "in") throw new Error("在 out 模式下设置值");
        if (socket.isComplex === false) {
            socket.value = value;
        } else {
            if (!valueIndex) throw new Error("未设置值索引");
            socket.value[valueIndex] = value;
        }
        node.nodeFunction();

        return outLineIndexs;
    }

    /**
     * 改变线所在插槽
     * @param id 线id
     * @param lineDefine 线定义
     */
    changeLineSocket(id: IDentity, lineDefine: LineDefine) {
        this.removeLine(id);
        this.addLine(lineDefine);
    }

    /**
     * 判断是否可以添加线
     * @param lineDefine 线定义
     * @returns 判断结果
     */
    private canAddLine(lineDefine: LineDefine): boolean {
        let { headNodeID, tailNodeID, headSocketIndex, tailSocketIndex } =
            lineDefine;
        let handID = headNodeID;
        let tailID = tailNodeID;
        let headNode = this.nodeList[headNodeID];
        let tailNode = this.nodeList[tailNodeID];

        if (!headNode || !tailNode) {
            processError("传入了未定义的节点", true);
            return false;
        }

        let headSocket = headNode.sockets[headSocketIndex];
        let tailSocket = tailNode.sockets[tailSocketIndex];

        if (handID === tailID) {
            processError("自循环的连接", false);
            return false;
        }
        if (headSocket.mode === tailSocket.mode) {
            processError("同模式的插槽", false);
            return false;
        }
        if (this.acceptType(headSocket.type, tailSocket.type) === false) {
            processError("不接受的类型", false);
            return false;
        }
        if (this.acceptSocket(headSocket, headNode) === false) {
            processError("向简单插槽中添加复数线", false);
            return false;
        }
        if (this.isCyclicGroup(headNodeID) === true) {
            processError("成环的连接", false);
            return false;
        }

        return true;
    }

    /**
     * 可接受的插槽类型
     * @param headSocket 线头所在插槽
     * @param headNode 线头所在节点
     * @returns 判断结果
     */
    private acceptSocket(headSocket: Socket, headNode: Node) {
        if (headSocket.isComplex === true) {
            for (let index in headNode.inLines) {
                let lineID = headNode.inLines[index];
                let line = this.lineList[lineID];
                if (line.headSocketIndex !== headSocket.id) continue;
                return false;
            }
        }
        return true;
    }

    /**
     * 判断是否为可接受标签
     * @param handSocketTypes 头插槽可接受标签列表
     * @param tailSocketTypes 尾插槽可接受标签列表
     */
    private acceptType(handSocketTypes: string[], tailSocketTypes: string[]): boolean {
        for (let i = 0; i < handSocketTypes.length; i++) {
            for (let j = 0; j < handSocketTypes.length; j++) {
                if (handSocketTypes[i] === tailSocketTypes[j]) return true;
            }
        }
        return false;
    }

    /**
     * 判断是否为成环的连接
     * @param headNodeID 连线头节点 id
     * @returns 判断结果
     */
    private isCyclicGroup(headNodeID: IDentity): boolean {
        // TODO 先放这里 我也不知道能不能运行

        let headNode = this.nodeList[headNodeID];
        if (isEmpty(headNode.outLines) === true) return false;
        for (let index in headNode.outLines) {
            let lineID = index;
            let line = this.lineList[lineID];
            if (line.headNodeID === headNodeID) return true;
            this.isCyclicGroup(line.headNodeID);
        }
        return false;
    }

    /**
     * 执行状态
     * @param LineID 执行线 ID
     */
    execute(LineID: IDentity) {
        let line = this.lineList[LineID];
        let { headNodeID, tailNodeID, headSocketIndex, tailSocketIndex } = line;
        let handNode = this.nodeList[headNodeID];
        let handInLineIDs = handNode.inLines;
        let handOutLineIDs = handNode.outLines;

        // 判断节点依赖值是否为空
        if (
            this.isUndefined(
                LineID,
                headSocketIndex,
                headNodeID,
                handInLineIDs
            ) === true
        )
            return;

        let tailNode = this.nodeList[tailNodeID];
        let handNodeSocket = handNode.sockets[headSocketIndex];
        let tailNodeSocket = tailNode.sockets[tailSocketIndex];
        let tailInLines = tailNode.inLines;

        // 如果头节点为纯输出节点，则尝试运行头节点
        if (isEmpty(tailInLines) === true) tailNode.nodeFunction();
        // 尝试为头节点插槽赋值 分别处理复杂节点与简单节点
        if (handNodeSocket.isComplex === false) {
            handNodeSocket.value = tailNodeSocket.value;
        } else {
            handNodeSocket.value[LineID] = tailNodeSocket.value;
        }
        // 运行节点
        handNode.nodeFunction();
        // 如果有 outLines，递归
        if (isEmpty(handOutLineIDs) === false) {
            for (let otherLine in handOutLineIDs) {
                this.execute(otherLine);
            }
        }
    }

    /**
     * 驳回缺乏节点插槽值定义的节点的执行
     */
    private isUndefined(
        LineID: IDentity,
        headSocketIndex: IDentity,
        headNodeID: IDentity,
        handInLineIDs: { [id: string]: IDentity }
    ): boolean {
        for (let handInLineID in handInLineIDs) {
            let line = this.lineList[handInLineID];
            let otherHeadSocketIndex = line.headSocketIndex;

            // 是本轮要被赋值的插槽
            if (headSocketIndex === otherHeadSocketIndex) continue;

            let handNode = this.nodeList[headNodeID];
            let otherHandNodeSocket = handNode.sockets[otherHeadSocketIndex];

            // 分别处理简单节点和复杂节点 被定义可以为空时能够执行
            if (otherHandNodeSocket.isComplex === false) {
                if (otherHandNodeSocket.valueCanNull === true) continue;
                if (otherHandNodeSocket.value === undefined) return true;
            } else {
                if (otherHandNodeSocket.valueCanNull === true) continue;
                if (otherHandNodeSocket.value[LineID]) return true;
            }
        }
        return false;
    }
}

export default State;
